package com.atguigu.gulimail.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NostockException;
import com.atguigu.common.service.cart_order.OrderCartDubboService;
import com.atguigu.common.service.member.OrderMemberService;
import com.atguigu.common.service.product.ProviderProductService;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberRespVo;
import com.atguigu.common.vo.SeckillOrderTo;
import com.atguigu.gulimail.order.constant.OrderConstant;
import com.atguigu.gulimail.order.dao.OrderDao;
import com.atguigu.gulimail.order.entity.OrderEntity;
import com.atguigu.gulimail.order.entity.OrderItemEntity;
import com.atguigu.gulimail.order.enume.OrderStatusEnum;
import com.atguigu.gulimail.order.feign.WareOrderFeignService;
import com.atguigu.gulimail.order.interceptor.OrderInterceptor;
import com.atguigu.gulimail.order.service.OrderItemService;
import com.atguigu.gulimail.order.service.OrderService;
import com.atguigu.gulimail.order.to.OrderCreateTo;
import com.atguigu.gulimail.order.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

//@RabbitListener(queues = {"atguigu.news"})
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {


    @DubboReference(check = false)
    OrderMemberService orderMemberdubbo;

    @DubboReference(check = false)
    OrderCartDubboService orderCartDubboService;

    @Autowired
    OrderItemService orderItemService;

    @DubboReference(check = false)
    ProviderProductService providerProductService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    StringRedisTemplate redisTemplate;


    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WareOrderFeignService wareOrderFeignService;


    private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal();


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );
        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() {
        MemberRespVo memberRespVo = OrderInterceptor.loginUser.get();
        OrderConfirmVo confirmVo = new OrderConfirmVo();

        Long id = memberRespVo.getId();

        CompletableFuture<Void> runAsync1 = CompletableFuture.runAsync(() -> {
            //1,远程查询所有的收货地址列表
            R address = orderMemberdubbo.getAddress(id);
            if (address.getCode() == 0) {
                List<MemberAddressVo> data = address.getData(new TypeReference<List<MemberAddressVo>>() {
                });
                confirmVo.setAddress(data);
            }
        }, executor);


        CompletableFuture<Void> runAsync2 = CompletableFuture.runAsync(() -> {
            //2,远程查询所有的选中的购物项
            R orderItem = orderCartDubboService.getOrderItem(id);
            if (orderItem.getCode() == 0) {
                List<OrderItemVo> data = orderItem.getData(new TypeReference<List<OrderItemVo>>() {
                });
                confirmVo.setItems(data);
            }
        }, executor).thenRunAsync(()->{

            List<OrderItemVo> items = confirmVo.getItems();
            List<Long> collect = items.stream().map((item) -> {
                return item.getSkuId();
            }).collect(Collectors.toList());
            //TODO  这里有个远程查库存的方法，只要调用一下dubbo就行了，但最近准备面试暂且放这
            //TODO  暂且搁置的业务还有一个模拟运费的操作，切换地址可以自动扣除运费

        },executor);

        //3,查询用户积分
        confirmVo.setIntegration(memberRespVo.getIntegration());


        //5,防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");

        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId(),token,30, TimeUnit.MINUTES);
        System.out.println(token);
        confirmVo.setOrderToken(token);

        try {
            CompletableFuture.allOf(runAsync1,runAsync2).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        return confirmVo;

    }

    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) throws Exception {
        MemberRespVo memberRespVo = OrderInterceptor.loginUser.get();
        confirmVoThreadLocal.set(orderSubmitVo);

        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        //下单：创建订单，验令牌，验价格，锁库存

        //1、验证令牌【令牌的对比和删除必须保证原子性】
        //0令牌失败 - 1删除成功
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        String orderToken = orderSubmitVo.getOrderToken();

        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()), orderToken);

        if(result == 1l){
            //成功
            //1,创建订单，订单项等信息
            OrderCreateTo createTo = createOrder();
            //2,验价
            BigDecimal payAmount = createTo.getOrder().getPayAmount();
            BigDecimal payPrice = orderSubmitVo.getPayPrice();
            //金额比对
            if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){

                //3,保存订单
                saveOrder(createTo);

                //4,库存锁定，只要有异常回滚订单数据
                WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();

                wareSkuLockVo.setOrderSn(createTo.getOrder().getOrderSn());

                List<OrderItemVo> collect = createTo.getOrderItems().stream().map((item) -> {
                    OrderItemVo itemVo = new OrderItemVo();
                    itemVo.setSkuId(item.getSkuId());
                    itemVo.setCount(item.getSkuQuantity());
                    itemVo.setTitle(item.getSkuName());

                    return itemVo;
                }).collect(Collectors.toList());

                wareSkuLockVo.setLocks(collect);

                //远程锁库存
                R r = wareOrderFeignService.orderStockLock(wareSkuLockVo);
                if(r.getCode()==0){
                    //锁成功
                    responseVo.setOrder(createTo.getOrder());
                    responseVo.setCode(0);

                    rabbitTemplate.convertAndSend("order-event-exchange","orde.create.order",createTo.getOrder());

                    return responseVo;
                }else {
                    //锁失败
                    throw new NostockException(createTo.getOrder().getId());

                }


            }else{

                responseVo.setCode(2);
                return responseVo;

            }

        }else{
            //失败
            responseVo.setCode(1);
            return responseVo;
        }

    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {

        //查询当前订单的最新状态
        OrderEntity byId = this.getById(orderEntity.getId());
        if(byId.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()){
            OrderEntity entity = new OrderEntity();
            entity.setId(byId.getId());
            entity.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(entity);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(entity,orderTo);

            rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderTo);

        }

    }

    @Override
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {

        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setMemberId(seckillOrderTo.getMemberId());

        BigDecimal multiply = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal("" + seckillOrderTo.getSeckillPrice()));
        orderEntity.setPayAmount(multiply);

        this.save(orderEntity);

        //保存订单项信息
        OrderItemEntity itemEntity = new OrderItemEntity();
        itemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        itemEntity.setRealAmount(multiply);

        itemEntity.setSkuQuantity(seckillOrderTo.getNum());

        orderItemService.save(itemEntity);

    }

    private void saveOrder(OrderCreateTo createTo) {
        OrderEntity order = createTo.getOrder();
        this.save(order);

        List<OrderItemEntity> orderItems = createTo.getOrderItems();
        orderItemService.saveBatch(orderItems);

    }

    private OrderCreateTo createOrder() throws Exception {

        OrderCreateTo createTo = new OrderCreateTo();

        String timeId = IdWorker.getTimeId();

        //构建订单
        OrderEntity orderEntity = buildOrder(timeId);

        //构建订单项
        List<OrderItemEntity> orderItemEntities = buildOrderItems(timeId);

        //计算价格、积分等相关
        computePrice(orderEntity,orderItemEntities);

        createTo.setOrder(orderEntity);
        createTo.setOrderItems(orderItemEntities);

        return createTo;
    }

    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
        BigDecimal total = new BigDecimal("0.0");

        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal integration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");

        BigDecimal gift = new BigDecimal("0.0");
        BigDecimal growth = new BigDecimal("0.0");
        //订单的总额，叠加每一个订单项的总额信息
        if(orderItemEntities != null){

            for (OrderItemEntity entity : orderItemEntities) {
                coupon = coupon.add(entity.getCouponAmount());
                integration = integration.add(entity.getIntegrationAmount());
                promotion = promotion.add(entity.getPromotionAmount());
                total = total.add(entity.getRealAmount());
                gift = gift.add(new BigDecimal(entity.getGiftIntegration().toString()));
                growth = growth.add(new BigDecimal(entity.getGiftGrowth().toString()));

            }
        }

        //1、订单价格相关
        orderEntity.setTotalAmount(total);
        //应付总额
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(integration);
        orderEntity.setCouponAmount(coupon);

        //设置积分等信息
        orderEntity.setIntegration(gift.intValue());
        orderEntity.setGrowth(growth.intValue());
        orderEntity.setDeleteStatus(0);//未删除

    }


    private OrderEntity buildOrder(String timeId) {
        OrderEntity orderEntity = new OrderEntity();
        OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();
        MemberRespVo memberRespVo = OrderInterceptor.loginUser.get();

        orderEntity.setMemberId(memberRespVo.getId());
        orderEntity.setOrderSn(timeId);

        //远程获取收货地址信息
        R fare = wareOrderFeignService.getFare(orderSubmitVo.getAddrId());
        FareVo fareResp = fare.getData(new TypeReference<FareVo>() {
        });

        //设置运费信息
        orderEntity.setFreightAmount(fareResp.getFare());
        //设置收货人信息
        orderEntity.setReceiverCity(fareResp.getAddress().getCity());
        orderEntity.setReceiverDetailAddress(fareResp.getAddress().getDetailAddress());
        orderEntity.setReceiverName(fareResp.getAddress().getName());
        orderEntity.setReceiverPhone(fareResp.getAddress().getPhone());
        orderEntity.setReceiverPostCode(fareResp.getAddress().getPostCode());
        orderEntity.setReceiverProvince(fareResp.getAddress().getProvince());
        orderEntity.setReceiverRegion(fareResp.getAddress().getRegion());


        //设置订单的相关状态信息
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);

        return orderEntity;
    }

    private List<OrderItemEntity> buildOrderItems(String id) throws Exception {
        List<OrderItemEntity> collect = null;

        MemberRespVo memberRespVo = OrderInterceptor.loginUser.get();


        R currentUserCartItems =  orderCartDubboService.getCurrentUserCartItems(memberRespVo.getId());

        if(currentUserCartItems != null && currentUserCartItems.size()>0){
            List<OrderItemVo> data = currentUserCartItems.getData(new TypeReference<List<OrderItemVo>>() {
            });

            collect = data.stream().map((item) -> {
                OrderItemEntity itemEntity = buildOrderItem(item);
                itemEntity.setOrderSn(id);
                return itemEntity;
            }).collect(Collectors.toList());

        }

        return collect;
    }

    private OrderItemEntity buildOrderItem(OrderItemVo item) {

        OrderItemEntity itemEntity = new OrderItemEntity();
        //1、订单信息：订单号 v

        //2、商品的SPU信息  V
        R spuInfoBySkuId = providerProductService.getSpuInfoBySkuId(item.getSkuId());
        SpuInfoEntity data = spuInfoBySkuId.getData(new TypeReference<SpuInfoEntity>() {
        });
        itemEntity.setSpuId(data.getId());
        itemEntity.setSpuBrand(data.getBrandId().toString());
        itemEntity.setSpuName(data.getSpuName());
        itemEntity.setCategoryId(data.getCatalogId());

        //3、商品的sku信息
        itemEntity.setSkuId(item.getSkuId());
        itemEntity.setSkuName(item.getTitle());
        itemEntity.setSkuPic(item.getImage());
        itemEntity.setSkuPrice(item.getPrice());
        String s = StringUtils.collectionToCommaDelimitedString(item.getSkuAttr());
        itemEntity.setSkuAttrsVals(s);
        itemEntity.setSkuQuantity(item.getCount());

        //4、优惠信息[不做]

        //5、积分信息
        itemEntity.setGiftGrowth(item.getPrice().multiply(new BigDecimal(item.getCount().toString())).intValue());
        itemEntity.setGiftIntegration(item.getPrice().multiply(new BigDecimal(item.getCount().toString())).intValue());
        //6、订单项的价格信息
        itemEntity.setPromotionAmount(new BigDecimal("0"));
        itemEntity.setCouponAmount(new BigDecimal("0"));
        itemEntity.setIntegrationAmount(new BigDecimal("0"));
        //当前订单项的实际金额。 总额-各种优惠
        BigDecimal orign = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
        BigDecimal subtract = orign.subtract(itemEntity.getCouponAmount())
                .subtract(itemEntity.getPromotionAmount())
                .subtract(itemEntity.getIntegrationAmount());
        itemEntity.setRealAmount(subtract);


        //6、订单项的价格信息
        //当前订单项的实际金额。 总额-各种优惠

        return itemEntity;
    }

}
































/**
 @RabbitHandler
 public void rabbitListener(Message messaege, OrderEntity orderEntity, Channel channel){

 //消息体
 byte[] body = messaege.getBody();
 //消息头
 MessageProperties messageProperties = messaege.getMessageProperties();

 long deliveryTag = messaege.getMessageProperties().getDeliveryTag();

 try {
 channel.basicAck(deliveryTag,false);
 } catch (IOException e) {
 e.printStackTrace();
 }

 System.out.println("消息的类型==》"+orderEntity.getClass());

 }


 @RabbitHandler
 public void rabbitListener2(OrderSettingEntity orderEntity){


 System.out.println("消息的类型==》"+orderEntity.getClass());

 }
 */